package test.file;

import static test.file.ReportProcessorConstant.ELEMENT_TYPE_CONSTANT;
import static test.file.ReportProcessorConstant.ELEMENT_TYPE_METHOD;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;

public class ReportMethodCleaner implements ReportLogicalWriter {

	@Override
	public void write(MultiValueMap<String, String> info, String encoding) throws Exception {
		// 计算出每一个文件需要删除的所有行号
		System.out.println(">>>>>>>>>> Calculate Line Numbers Per File <<<<< <<<<<");
		MultiValueMap<String, Integer> lineNosPerFile = calculateLineNosPerFile(info, encoding);
		// 移除无用方法
		System.out.println(">>>>>>>>>> Remove Unused Method <<<<< <<<<<");
		removeUnusedMethod(lineNosPerFile,encoding);
	}
	
	public  MultiValueMap<String, Integer> calculateLineNosPerFile(MultiValueMap<String, String> processReport, String encoding) throws Exception {
		Iterator<String> iterator = processReport.keySet().iterator();
		// 统计每个文件需要删除的行数
		MultiValueMap<String, Integer> needToRemoveLineNosOfFile = new LinkedMultiValueMap<>();
		while (iterator.hasNext()) {
			String fullFilePath = (String) iterator.next();
			// System.out.println(fullFilePath);///**/WarnService.java
			List<String> methodAndLineNoList = processReport.get(fullFilePath);
			for (String methodAndLineNo : methodAndLineNoList) {
				String[] split = methodAndLineNo.split("\\$");
				String method = split[0];
				String lineNo = split[1];
				String elemtnType = split[2];
				List<Integer> findNeedToRemoveLineNos = findNeedToRemoveLineNosOfMethod(new File(fullFilePath), encoding, method, lineNo,elemtnType);
				List<Integer> old = needToRemoveLineNosOfFile.get(fullFilePath);
				List<Integer> newList = new ArrayList<>();
				if(CollectionUtils.isNotEmpty(findNeedToRemoveLineNos)){
					newList.addAll(findNeedToRemoveLineNos);
				}
				if(CollectionUtils.isNotEmpty(old)){
					newList.addAll(old);
				}
				needToRemoveLineNosOfFile.put(fullFilePath, newList);
			}
		}
		Iterator<String> iteratorOfFileLineNos = needToRemoveLineNosOfFile.keySet().iterator();
		while (iteratorOfFileLineNos.hasNext()) {
			String key = (String) iteratorOfFileLineNos.next();
			List<Integer> list = needToRemoveLineNosOfFile.get(key);
			Set needToRemoveLineNos = new HashSet<>(list);
			System.out.println("needToRemoveLineNos： " + key);
			System.out.println(needToRemoveLineNos);
		}
		return needToRemoveLineNosOfFile;
	}

	public  void removeUnusedMethod(MultiValueMap<String, Integer> lineNosPerFile, String encoding) throws IOException {
		Iterator<String> iterator = lineNosPerFile.keySet().iterator();
		while (iterator.hasNext()) {
			String filePath = (String) iterator.next();
			File targetFile = new File(filePath);
			List<String> contents = Files.readLines(targetFile, Charsets.UTF_8);
			List<Integer> needRemoveLineNoList = lineNosPerFile.get(filePath);
			// 重新写文件
			removeByLineNoList(contents, new HashSet<Integer>(needRemoveLineNoList), targetFile, encoding);
		}
	}
	

	public List<Integer> findNeedToRemoveLineNosOfMethod(File file, String encoding, String methodName, final String lineNo, final String elemtnType) throws Exception {
		// System.out.println(file.getName());
		
		if(elemtnType.equals(ELEMENT_TYPE_CONSTANT)){
			return new ArrayList<Integer>();
		}
		
		//一下元素类型是 Method
		List<String> contents = null;
		try {
			contents = Files.readLines(file, Charsets.UTF_8);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (CollectionUtils.isEmpty(contents)) {
			return Collections.EMPTY_LIST;
		}
		// 查找指定方法的行数 !
		final String boundryRegx = ".*\\b" + methodName + "\\b.*"; // 精确匹配方法名
		List<Integer> needRemoveLineNoList = Files.readLines(file, Charsets.UTF_8, new LineProcessor<List<Integer>>() {
			int currentLineNo = 0; // start at 1
			List<Integer> methodLines = Lists.newArrayList();
			Stack<String> curlyBrackets = new Stack<>();
			public boolean processLine(String line) {
				currentLineNo++;
				if(elemtnType.equals(ELEMENT_TYPE_METHOD)){
					// 开始找指定方法，并且开始记录行号
					if (methodLines.isEmpty()) {
						if (line.matches(boundryRegx) && lineNo.equals(String.valueOf(currentLineNo))) {
							methodLines.add(currentLineNo);
							// 开始匹配左花括号
							int countMatchesLeft = StringUtils.countMatches(line, "{");
							for (int i = 0; i < countMatchesLeft; i++) {
								curlyBrackets.push("{");
							}
						}
					} else {
						methodLines.add(currentLineNo);
						// 开始匹配左花括号
						int countMatchesLeft = StringUtils.countMatches(line, "{");
						for (int i = 0; i < countMatchesLeft; i++) {
							curlyBrackets.push("{");
						}
						// 开始匹配右花括号,需保证堆栈里面已经有左花括号
						if(!curlyBrackets.isEmpty()){
							int countMatchesRight = StringUtils.countMatches(line, "}");
							for (int i = 0; i < countMatchesRight; i++) {
									curlyBrackets.pop();
							}
							if (curlyBrackets.isEmpty()) {
								return false; //跳出处理
							}
						}
					}
				}

				return true;
			}

			public List<Integer> getResult() {
				return methodLines;
			}
		});
		// 查找指定方法的注释的行数
		if (CollectionUtils.isNotEmpty(needRemoveLineNoList)) {
			Integer methodLineNo = needRemoveLineNoList.get(0);
			while (true) {
				methodLineNo--;
				String previousString = contents.get(methodLineNo - 1); // list index start at 0
				if (StringUtils.isNotBlank(previousString)) {
					boolean containsNone = StringUtils.containsNone(previousString, "*/\\@");
					boolean containsA = StringUtils.contains(previousString, "}");
					boolean containsB = StringUtils.contains(previousString, ";");
					if (containsNone && (containsA || containsB)) {
						break;
					}
				}
				needRemoveLineNoList.add(methodLineNo);
			}
		}
		// System.out.println(needRemoveLineNoList);
		return needRemoveLineNoList;
	}

	public  void removeByLineNoList(List<String> contents, final Set<Integer> lineNoList, File file, String encoding) throws IOException {
		Collection<String> filterList = Collections2.filter(contents, new Predicate<String>() {
			int lineCount = 0;

			@Override
			public boolean apply(String input) {
				lineCount++;
				if (lineNoList.contains(lineCount)) {
					return false;
				}
				return true;
			}
		});
		FileUtils.writeLines(file, encoding, filterList, false);
	}
	
}
